#ifndef BLOQUEINTERNO_H_
#define BLOQUEINTERNO_H_

#include "BloqueArbol.h"
#include "Arbol.h"
class BloqueHoja;
class TipoClave;

class BloqueInterno : public BloqueArbol{
private:

    /*Retorna el hijo que contiene las claves mas chicas*/
    BloqueArbol* getHijoMenor();


    /*Paso una clave que se agregara en la lista de clave en la posicion donde quede ordenada con las otras claves
     * (de menor a mayor)
     * En posicion voy a retornar la posicion dentro del vector donde guardara esa clave.
     * Se retorna un resultado que podra ser: OK o Duplicado si la clave ya se encontraba. */
    /*pre: YA SE QUE LA CLAVE DEBE ESTAR EN ESTE NODO*/
      Resultado addClave(const Key &clave, int& posicion);

    /*remueve una clave de su vector de claves, se usa en remover clave e hijo, se retorna en posicionDondeAgregar
     * en que posicion del vector se agrego el vector
     * se retorna RES_OK o RES_INEXISTENTE si no existe la clave que se desea borrar*/
    virtual Resultado removerClave(const Key& clave, int& posicionDondeAgregar);

    /*De dos nodos (this y nodoHermano, que es el de la derecha) me quedo con solo uno y le seteo borrado = true al nodo del medio.
     * nodoHermano queda con todas las claves e hijos de ambos.*/
    bool dosAUno(BloqueInterno* nodoHermano);

    /*retorna true si el nodo contiene la clave en su vector de claves*/
    bool tengoClave(Key clave);

public:
	/*
	 * Pre: -
	 * Pos: Crea una nueva instancia.
	 */
	BloqueInterno(Arbol* a,cantBytes tamanio,TipoClave* extractor);

	/*
	 * Pre: -
	 * Pos: Libera los recursos instanciados.
	 */
	virtual ~BloqueInterno();


	/*PRE: debe recibirlos en el orden que se balancearan
	     *se retorna el resultado en res y el nuevo padre*/
	    BloqueInterno* mergeRaizHoja(BloqueHoja* hermanoIzq, BloqueHoja* nodo, BloqueHoja* hermano, Resultado res);

    /*agrego a mi lista de hijos el que serÃ­a mi Ãºltimo hijo (no tiene clave asociada)
     * pre: ya se que el que agrego es el ultimo, ultimo en el sentido que sus claves son mayores a las de todos
     * los hermanos*/
    void addUltimoHijo(offset hijo);

    /*retorna el numero de la posicion dentro del vector donde se encuentra el hijo
     * PRE: debe estar el hijo dentro del vector*/
    int getPosicionVector(offset hijo);

    /*se retorna el hermano que tiene mi hijo "hijo" a la izquierda, si tengo, sino retorno Null*/
   BloqueArbol* getHijoIzquierdo(offset hijo);

    /*se retorna el hermano que tiene mi hijo "hijo" a la derecha, si tengo, sino retorno Null*/
   BloqueArbol* getHijoDerecho(offset hijo);

   /*se retorna el hermano que tengo a la izquierda, si tengo, sino retorno Null*/
         BloqueArbol* getHijoIzquierdo(const Key& claveVieja);

       /*se retorna el hermano que tengo a la derecha, si tengo, sino retorno Null*/
         BloqueArbol* getHijoDerecho(const Key& claveVieja);

       /*se retorna el hermano derecho que tiene mi hermano derecho, si tengo, sino retorno Null*/
       /*pre: tener hermano derecho*/
         BloqueArbol* getHijoDerDer(const Key& claveVieja);

       /*se retorna el hermano izquierdo que tiene mi hermano izquierdo, si tengo, sino retorno Null*/
       /*pre: tener hermano izquierdo*/
         BloqueArbol* getHijoIzqIzq(const Key& claveVieja);

   /*Con el puntero a quien sera mi nuevo hijo y la clave correspondiente al mismo,
       * agrego ambos a mi nodo. El hijo surge desde el tener que agregar un clave
       * (al agregar una nueva clave, genero una particion de los intervalos entre los cuales derivos las claves)*/
      /*ANTES DEBO SABER QUIEN SERA EL HIJO, O SI DEBO CREARLO*/
      /*si hay ovewflow se manejarÃ¡ y se retornara el resultado de dicha operacion: ok o duplicada*/
      Resultado addClaveEHijo(const Key &clave,offset hijo);

    /*Con el puntero al hijo que debo borrar y la clave correspondiente al mismo,
     * saco a ambos del vector correspondiente.
    *ANTES DEBO SABER QUIEN SERA EL HIJO (yo no relaciono hijo y clave)
    *si hay underflow se manejarÃ¡ y se retornara el resultado de dicha operacion: ok o inexistente*/
    Resultado removerClaveEHijo(const Key &clave, BloqueArbol* hijo);

    /*remuevo de mi lista de hijos un hijo que tiene la clave pasada por parametro.
     * pre: se sabe que tengo ese hijo*/
    void removerHijo(const Key & claveVieja);

    /*se invoca overflowInterno o overflowRaizInterno segun tenga o no padre*/
    BloqueInterno* resolverOverflow();

    /* En este metodo se resuelve el underflow invocando diferentes metodos
        * segun la estructura del arbol en ese momento. En clave vieja se
        * incluye la clave que se borro generando el underflow, en res, retorno
        * si se logrÃ³ realizar la operacion*/
    void resolverUnderflow(Resultado& res, const Key& claveViejaNodo);


    /*Se inserta un elemento en el hijo hoja correspondiente, y en res se retorna RES_OK o RES_DUPLICADO si la clave ya estaba.
     * En el retorno se retorna this. se indica si hubo o no overflow al insertar el elemento*/
    BloqueArbol* insertarElemento(const Elemento& elemento, Resultado& res, bool& overflow);

    /*Recursivamente se busca donde borrar el elemento, se retorna this y el resultado: RES_OK u RES_IXEISTENTE*/
    BloqueArbol* removerElemento(const Elemento& elemento, Resultado& res);

    /*le paso una clave y recorre el arbol recursivamente,  hasta llegar a una hoja que retorna
     * el Elemento correspondiente , si no lo encuentra, retorna null pointer*/
    offset getElemento(const Key& clave, Resultado& res);

    /*Le paso una clave que estoy buscando, el me devolvera el puntero correspondiente al hijo hoja donde yo deberia
     *  buscar la clave*/
   BloqueArbol* buscarHijo(const Key& clave);

    /*Le paso una clave que estoy buscando, el me devolvera el puntero correspondiente al hijo hoja u interno (es inmediato siguiente)\
     *  donde yo debere buscar la clave
     * posicion es de retorno, donde dentro del vector de claves esta la clave a buscar*/
    BloqueArbol* buscarUnHijo(const Key& claveABuscar, unsigned& posicion);


    /*Se maneja la situacion en que  un nodo raiz se encuentra en overflow,
     * creando su nuevo hermano y al nuevo padre(raiz), se reorna la nueva raiz
     * y el resultado de la operacion*/
    BloqueInterno* overflowRaizInterno(Resultado& res);

    /*Se intenta balancear de diferentes formas segun la estructura del arbol
     * res se modifica, segun como suceden las operaciones en el arbol*/
    void overflowInterno(Resultado& res);



  /*si puede balancear los nodos, lo hace
    	 * y retorna true, sino puede retorna false. El nodo que invoca es el padre
    	*PRE: debe recibirlos en el orden que se balancearan
    	 * se usa solo para undeflows*/
    bool mergeDosInternos(BloqueInterno* nodo, BloqueInterno* hermano);

    /*igual al mergeDosInternos pero para overflows
     *si puede balancear los nodos, lo hace
     * y retorna true, sino puede retorna false. El nodo que invoca es el padre
     *PRE: debe recibirlos en el orden que se balancearan*/

   bool balancearDosInternos(BloqueInterno* nodo, BloqueInterno* hermano);

    /*El nodo que invoca es el padre de los tres.
     * Se redistribuyes las claves en sÃ³lo dos nodos, y se borra el nodo del medio
    *PRE: debe recibirlos en el orden que se balancearan
    *indico en el campo 3a2 : true para underflow y false para overflow*/
    Resultado mergeTresInternos(BloqueInterno* hermanoIzq, BloqueInterno* nodo, BloqueInterno* hermano, bool tresADos);

    /*se retorna la clave mayor de toda la rama desde este nodo*/
    Key getMayorDeRama();

    /*retorna las claves en un string*/
    virtual std::string clavesToString();

    /*se imprimen las claves del vector por consola*/
    virtual void imprimir();



	/*
	 * Pre: -
	 * Pos: devuelve el tipo de bloque 'i'.
	 */
	virtual char getTipo();

	/*
	 * Pre: -
	 * Pos: serializa el estado el objeto.
	 */
	virtual Bytes serializarse();

	/*
	 * Pre: -
	 * Pos: hidrata el estado del objeto.
	 */
	virtual bool hidratarse(const Bytes& bytes);


};

#endif /* BLOQUEINTERNO_H_ */
